﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
using System.Security.Claims;

namespace SBlogCore.Common.AuthorizationExtend
{
    [SkipScan]
    public static class AuthorizationExtensions
    {
        //授权策略
        public static void AddAuthorizationSetUp(this IServiceCollection services,IEnumerable<Assembly> assemblies)
        {
            var registerAuthorizeTypes = assemblies.SelectMany(s => s.GetTypes().Where(w => w.IsAssignableTo(typeof(ControllerBase))));

            //dic的Key是控制器的AuthorizeAttribute，Value是方法的AuthorizeAttribute
            Dictionary<string,HashSet<string>> policyDic = new Dictionary<string, HashSet<string>>();

            foreach (var authorizeType in registerAuthorizeTypes)
            {
                //默认一个控制器只会保留一个策略
                string controllerPolicy =
                    authorizeType.GetCustomAttributes<AuthorizeAttribute>()
                        .FirstOrDefault(w => !string.IsNullOrEmpty(w.Policy))?.Policy ?? string.Empty;

                if (string.IsNullOrEmpty(controllerPolicy)) continue;

                HashSet<string> methodPoliceList = new HashSet<string>();
                var methodPolitics = authorizeType.GetMethods().Select(s =>
                    s.GetCustomAttributes<AuthorizeAttribute>().FirstOrDefault(w => !string.IsNullOrEmpty(w.Policy))
                        ?.Policy ?? string.Empty);
                foreach (var item in methodPolitics)
                {
                    if (!string.IsNullOrEmpty(item))
                    {
                        methodPoliceList.Add(item);
                    }
                }

                policyDic.Add(controllerPolicy, methodPoliceList);
            }
            services.AddAuthorization(options =>
            {
                foreach (var item in policyDic)
                {
                    var controllerAuthor = item.Key;
                    var methodAuthor = item.Value;
                    //注册控制器策略
                    RegisterPolicy(options, controllerAuthor);
                    foreach (var ma in methodAuthor)
                    {
                        //注册方法策略
                        RegisterPolicy(options, $"{controllerAuthor}:{ma}");
                    }
                }
            });
        }
        /// <summary>
        /// 自定义策略
        /// </summary>
        /// <param name="options"></param>
        /// <param name="policy"></param>
        private static void RegisterPolicy(AuthorizationOptions options, string policy)
        {
            options.AddPolicy(policy, po => po.RequireAssertion(context =>
            {
                //存储用户所有权限的集合
                //这里可以控制方法级的权限
                ClaimsPrincipal user = context.User;
                //Claims不存在值说明没有登录，直接抛出
                if (!user.Claims.Any()) return false;
                //获取用户所有的角色集合
                var roles = user.Claims.First(w => w.Type == ClaimTypes.Role).Value;
                var userRoleList = roles.Split(',');
                //判断是否包含当前所需要的角色代码
                return userRoleList.Contains(policy) || userRoleList.Contains("Admin");

            }));
        }
    }
}
